package ddz;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import com.kaka.Startup;
import com.kaka.notice.Facade;
import com.kaka.notice.FacadeFactory;
import com.kaka.notice.Message;
import com.kaka.util.ResourceUtils;
import ddz.constants.OpCode;
import ddz.db.dao.UserDao;
import ddz.db.redis.JedisFactory;
import ddz.net.CtxManager;
import ddz.net.EventQueueExecutor;
import ddz.net.ProtocolMessageHandler;
import ddz.net.WebSocketServer;
import ddz.numerical.ConfManager;
import ddz.scheduler.QuartzFacade;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

public class Main extends Startup implements Runnable {

    /**
     * 用代码初始化logback日志组件，防止出现烦人的有关logback初始化的日志信息
     */
    static void initLogback() {
        Logger logger = LoggerFactory.getLogger(Main.class);
        String path = ResourceUtils.getClassLoaderPath(Main.class);
        File file = new File(path + "/sys-config/logback.xml");
        InputStream is = null;
        if (!file.exists()) {
            is = ResourceUtils.getResourceAsStream("sys-config/logback.xml", Main.class);
            if (is == null) return;
        } else {
            try {
                is = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                logger.error(e.getLocalizedMessage(), e);
            }
        }
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        JoranConfigurator joranConfigurator = new JoranConfigurator();
        joranConfigurator.setContext(loggerContext);
        loggerContext.reset();
        try {
            joranConfigurator.doConfigure(is);
        } catch (Exception e) {
            logger.error(e.getLocalizedMessage(), e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    logger.error(e.getLocalizedMessage(), e);
                }
            }
        }
        //打印logback初始化时的信息
        //StatusPrinter.printIfErrorsOccured(loggerContext);
        //StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
    }

    private final ExecutorService threadPool;
    private WebSocketServer webSocketServer;
    private ch.qos.logback.classic.Logger logger;

    private Main() {
        initLogback();
        logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Main.class);
        QuartzFacade facade = FacadeFactory.config(QuartzFacade.class);
        this.threadPool = Executors.newCachedThreadPool();
        //配置异步事件线程池，全局仅设置一次
        facade.initThreadPool(threadPool);
    }

    /**
     * 解析本地静态数值配置表txt文件
     *
     * @throws Exception
     */
    protected void parseNumericConfig() throws Exception {
        String path = ResourceUtils.getClassLoaderPath(Main.class);
        File dir = new File(path + "/usr-config");
        File[] files = dir.listFiles();
        Facade facade = FacadeFactory.getFacade();
        for (File file : files) {
            try (InputStream is = new FileInputStream(file)) {
                ConfManager manager = facade.retrieveProxy(file.getName());
                if (manager != null) {
                    manager.parse(is, "UTF-8", 1);
                    if (logger.isInfoEnabled()) logger.info("配置文件解析完成：" + file.getName());
                }
            }
        }
    }

    protected void startUpTcpServer() {
        Facade facade = FacadeFactory.getFacade();
        facade.registerCommand(OpCode.core_cmd, ProtocolMessageHandler.class, -1);
        this.webSocketServer = new WebSocketServer(false);
        this.webSocketServer.start(new InetSocketAddress(8081), 12);
    }

    public static void main(String[] args) throws Exception {
        Main main = new ExitSys().init(new Main());
        main.scan("ddz.logic", "ddz.db.dao", "ddz.db.service", "ddz.numerical.manager");
        main.parseNumericConfig();

        QuartzFacade facade = FacadeFactory.getFacade();
        facade.registerProxy(EventQueueExecutor.class);
        facade.sendMessage(new Message(OpCode.timer_writeto_db), "data_queue", "*/5 * * * * ?");
        facade.sendMessage(new Message(OpCode.timer_player_match), "matcher", "*/2 * * * * ?");
//        facade.sendMessage(new Message(OpCode.timer_init_recharge_rank), "recharge_rank", "0 0 0 * * ?");
//        facade.sendMessage(new Message(OpCode.timer_day_rank_rewards), "day_rank_rewards", "58 59 23 * * ?"); //每天23:59:58秒执行
//        facade.sendMessage(new Message(OpCode.timer_mon_rank_rewards), "mon_rank_rewards", "59 59 23 L * ?"); //每月月末23:59:59秒执行
        UserDao userDao = facade.retrieveProxy(UserDao.class);
        //userDao.initWinRank();
        userDao.initRobotUidList();

        //facade.sendMessage(new Message(OpCode.cmd_test_game));

        main.startUpTcpServer();
    }

    @Override
    public void run() {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>准备停止服务");
        Set<Long> uidList = null;
        if (this.webSocketServer != null) {
            CtxManager ctxManager = CtxManager.instance();
            List<ChannelHandlerContext> list = ctxManager.channelList();
            uidList = new HashSet<>(list.size());
            for (ChannelHandlerContext ctx : list) {
                Object bindData = CtxManager.getBindId(ctx);
                if (bindData != null) {
                    Class<?> type = bindData.getClass();
                    if (type.equals(Long.TYPE) || type.equals(Long.class)) {
                        uidList.add((Long) bindData);
                    }
                }
            }
            this.webSocketServer.stop();
            CtxManager.instance().close();
            logger.info("已关闭WebSocket服务...");
        }
        QuartzFacade facade = FacadeFactory.getFacade();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
        }
        facade.sendMessage(new Message(OpCode.timer_writeto_db, true));
        logger.info("已清空数据队列并已写入数据库...");
        logger.info("已停止redis订阅...");
        JedisFactory.getFactory().destroy();
        logger.info("已关闭所有redis连接...");
        if (threadPool != null) {
            logger.info("准备关闭事件领域线程池...");
            threadPool.shutdown();
            try {
                logger.info("等待子线程关闭...");
                threadPool.awaitTermination(20, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                logger.error("关闭事件领域线程池异常：{}", e.getLocalizedMessage());
            } finally {
                logger.info("已关闭事件领域线程池...");
            }
        }
        facade.shutdownScheduler();
        logger.info("已关闭定时调度器...");
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>服务已停止");
    }
}
